<!DOCTYPE html>
<html lang="en">
<head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
                                <title>Martin's Hopalong Orbits Visualizer - WebGL Experiment</title>
                                <meta charset="utf-8">
                                <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
                                <style>
                                body {
                                background-color: #000000;
                                margin: 0px;
                                overflow: hidden;
                                font-family:Monospace;
                                font-size:13px;
                                text-align:center;
                                font-weight: bold;
                                text-align:center;
                                }

                                a {
                                color:#ffffaa;
                                }

#info {
                                color: #fff;
                                position: absolute;
                                top: 0px; width: 100%;
                                padding: 5px;
                                z-index: 100;
                                }
#plusone {
                                position: absolute;
                                bottom: 5px;
                                left: 10px;
                                z-index: 100;
                                }
#tweet {
                                position: absolute;
                                bottom: 7px;
                                left: 90px;
                                z-index: 100;
                                }

#about {
                                text-align:left;
                                z-index:110;
                                position:absolute;
                                top: 25px;
                                left: 5px;
                                display:none;
                                width: 400px;
                                color:#fff; background: #121212; opacity:0.8; border-radius:20px; padding: 32px
                                }
#aboutlink {
                                z-index:110;
                                position:absolute;
                                top: 5px;
                                left: 5px;
                                }
                                </style>
                                </head>
                                <body>

                                <script src="./Martin's Hopalong Orbits Visualizer - WebGL Experiment_files/Three.js"></script>
                                <script src="./Martin's Hopalong Orbits Visualizer - WebGL Experiment_files/Stats.js"></script>
                                <script src="./Martin's Hopalong Orbits Visualizer - WebGL Experiment_files/Detector.js"></script>
                                <script src="./Martin's Hopalong Orbits Visualizer - WebGL Experiment_files/RequestAnimationFrame.js"></script>

                                <script>
                                /*
                                * AUTHOR: Iacopo Sassarini
                                */

                                if ( ! Detector.webgl ) Detector.addGetWebGLMessage();

                                var SCALE_FACTOR = 1000;
                                var CAMERA_BOUND = 200;

                                var NUM_POINTS_SUBSET = 40000;
                                var NUM_SUBSETS       = 7;
                                var NUM_POINTS = NUM_POINTS_SUBSET * NUM_SUBSETS;

                                var NUM_LEVELS = 5;
                                var LEVEL_DEPTH = 400;

                                var DEF_BRIGHTNESS = 1;
                                var DEF_SATURATION = 0.7;

                                // Orbit parameters constraints
                                var A_MIN = -30;
                                var A_MAX = 30;
                                var B_MIN = .1;
                                var B_MAX = 1.5;
                                var C_MIN = 8;
                                var C_MAX = 17;
                                var D_MIN = 0;
                                var D_MAX = 10;
                                var E_MIN = 0;
                                var E_MAX = 12;

                                // Orbit parameters
                                var a, b, c, d, e;

                                // Orbiut data
                                var orbit = {
                                subsets: [],
                                xMin: 0,
                                xMax: 0,
                                yMin: 0,
                                yMax: 0,
                                scaleX: 0,
                                scaleY: 0
                                }
                                // Initialize data points
                                for (var i = 0; i < NUM_SUBSETS; i++){
                                var subsetPoints = [];
                                for (var j = 0; j < NUM_POINTS_SUBSET; j++){
                                subsetPoints[j] = {
                                x: 0,
                                y: 0,
                                vertex:  new THREE.Vertex(new THREE.Vector3(0,0,0))
                                };
                                }
                                orbit.subsets.push(subsetPoints);
                                }

                                var container, stats;
                                var camera, scene, renderer, composer, hueValues = [];

                                var mouseX = 0, mouseY = 0;

                                var windowHalfX = window.innerWidth / 2;
                                var windowHalfY = window.innerHeight / 2;

                                var speed = 8;
                                var rotationSpeed = 0;

                                init();
                                animate();

                                function init() {

                                sprite1 = THREE.ImageUtils.loadTexture( "galaxy.png" );

                                container = document.createElement( 'div' );
                                document.body.appendChild( container );

                                camera = new THREE.PerspectiveCamera( 60, window.innerWidth / window.innerHeight, 1, 3 * SCALE_FACTOR );
                                camera.position.z = SCALE_FACTOR/2;

                                scene = new THREE.Scene();
                                scene.fog = new THREE.FogExp2( 0x000000, 0.0011);

                                generateOrbit();

                                for (var s = 0; s < NUM_SUBSETS; s++){hueValues[s] = Math.random();}

                                // Create particle systems
                                for (var k = 0; k < NUM_LEVELS; k++){
                                for (var s = 0; s < NUM_SUBSETS; s++){
                                var geometry = new THREE.Geometry();
                                for (var i = 0; i < NUM_POINTS_SUBSET; i++){geometry.vertices.push( orbit.subsets[s][i].vertex);}
                                var materials = new THREE.ParticleBasicMaterial( { size: (3 ), map: sprite1, blending: THREE.AdditiveBlending, depthTest: false, transparent : true } );
                                materials.color.setHSV(hueValues[s], DEF_SATURATION, DEF_BRIGHTNESS);
                                var particles = new THREE.ParticleSystem( geometry, materials );
                                particles.myMaterial = materials;
                                particles.myLevel = k;
                                particles.mySubset = s;
                                particles.position.x = 0;
                                particles.position.y = 0;
                                particles.position.z = - LEVEL_DEPTH * k - (s  * LEVEL_DEPTH / NUM_SUBSETS);
                                particles.needsUpdate = 0;
                                scene.add( particles );
                                }
                                }

                                // Setup renderer and effects
                                renderer = new THREE.WebGLRenderer( { clearColor: 0x000000, clearAlpha: 1, antialias: false } );
                                renderer.setSize( window.innerWidth, window.innerHeight );

                                container.appendChild( renderer.domElement );

                                stats = new Stats();
                                stats.domElement.style.position = 'absolute';
                                stats.domElement.style.bottom = '5px';
                                stats.domElement.style.right = '5px';
                                container.appendChild( stats.domElement );

                                // Setup listeners
                                document.addEventListener( 'mousemove', onDocumentMouseMove, false );
                                document.addEventListener( 'touchstart', onDocumentTouchStart, false );
                                document.addEventListener( 'touchmove', onDocumentTouchMove, false );
                                document.addEventListener( 'keydown', onKeyDown, false );
                                window.addEventListener( 'resize', onWindowResize, false );

                                // Schedule orbit refeneration
                                setInterval(updateOrbit, 7000);
                                }

                                function animate() {
                                requestAnimationFrame( animate );
                                render();
                                stats.update();
                                }

                                function render() {

                                if (camera.position.x >= - CAMERA_BOUND && camera.position.x <= CAMERA_BOUND){
                                camera.position.x += ( mouseX - camera.position.x ) * 0.05;
                                if (camera.position.x < - CAMERA_BOUND) camera.position.x = -CAMERA_BOUND;
                                if (camera.position.x >  CAMERA_BOUND) camera.position.x = CAMERA_BOUND;
                                }
                                if (camera.position.y >= - CAMERA_BOUND && camera.position.y <= CAMERA_BOUND){
                                camera.position.y += ( - mouseY - camera.position.y ) * 0.05;
                                if (camera.position.y < - CAMERA_BOUND) camera.position.y = -CAMERA_BOUND;
                                if (camera.position.y >  CAMERA_BOUND) camera.position.y = CAMERA_BOUND;
                                }

                                camera.lookAt( scene.position );

                                for( i = 0; i < scene.objects.length; i++ ) {
                                scene.objects[i].position.z +=  speed;
                                scene.objects[i].rotation.z += rotationSpeed;
                                if (scene.objects[i].position.z > camera.position.z){
                                scene.objects[i].position.z = - (NUM_LEVELS -1) * LEVEL_DEPTH;
                                if (scene.objects[i].needsUpdate == 1){
                                scene.objects[i].geometry.__dirtyVertices = true;
                                scene.objects[i].myMaterial.color.setHSV( hueValues[scene.objects[i].mySubset], DEF_SATURATION, DEF_BRIGHTNESS);
                                scene.objects[i].needsUpdate = 0;
                                }
                                }
                                }

                                renderer.render( scene, camera );
                                }

                                ///////////////////////////////////////////////
                                // Hopalong Orbit Generator
                                ///////////////////////////////////////////////
                                function updateOrbit(){
                                generateOrbit();
                                for (var s = 0; s < NUM_SUBSETS; s++){
                                hueValues[s] = Math.random();
                                }
                                for( i = 0; i < scene.objects.length; i++ ) {
                                scene.objects[i].needsUpdate = 1;
                                }

                                }

                                function generateOrbit(){
                                var x, y, z, x1;
                                var idx = 0;

                                prepareOrbit();

                                // Using local vars should be faster
                                var al = a;
                                var bl = b;
                                var cl = c;
                                var dl = d;
                                var el = e;
                                var subsets = orbit.subsets;
                                var num_points_subset_l = NUM_POINTS_SUBSET;
                                var num_points_l = NUM_POINTS;
                                var scale_factor_l = SCALE_FACTOR;

                                var xMin = 0, xMax = 0, yMin = 0, yMax = 0;

                                for (var s = 0; s < NUM_SUBSETS; s++){

                                // Use a different starting point for each orbit subset
                                x = s * .001;
                                y = s * .001;

                                var curSubset = subsets[s];

                                for (var i = 0; i < num_points_subset_l; i++){

                                // Iteration formula (generalization of the Barry Martin's original one)
z = (dl + Math.sqrt(Math.abs(bl * x - cl)));

    if (x > 0)
{
x1 = y - z;
}
else if (x == 0)
{
x1 = y;
}
else
{
x1 = y + z;
}
y = al - x;
    x = x1 + el;

        curSubset[i].x = x;
                         curSubset[i].y = y;

                                 if (x < xMin)
{
xMin = x;
}
else if (x > xMax)
{
xMax = x;
}
if (y < yMin)
{
yMin = y;
}
else if (y > yMax)
{
yMax = y;
}

idx++;
}
}

var scaleX = 2 * scale_factor_l / (xMax - xMin);
             var scaleY = 2 * scale_factor_l / (yMax - yMin);

                          orbit.xMin = xMin;
                                       orbit.xMax = xMax;
                                               orbit.yMin = yMin;
                                                       orbit.yMax = yMax;
                                                               orbit.scaleX = scaleX;
                                                                       orbit.scaleY = scaleY;

                                                                               // Normalize and update vertex data
                                                                               for (var s = 0;

                                                                               s < NUM_SUBSETS;
                                                                               s++)
{
var curSubset = subsets[s];

	for (var i = 0; i < num_points_subset_l; i++)
	{
		curSubset[i].vertex.position.x = scaleX * (curSubset[i].x - xMin) - scale_factor_l;
		curSubset[i].vertex.position.y = scaleY * (curSubset[i].y - yMin) - scale_factor_l;
	}
}
}

function prepareOrbit()
{
	shuffleParams();
	orbit.xMin = 0;
	orbit.xMax = 0;
	orbit.yMin = 0;
	orbit.yMax = 0;
}

function shuffleParams()
{
	a = A_MIN + Math.random() * (A_MAX - A_MIN);
	b = B_MIN + Math.random() * (B_MAX - B_MIN);
	c = C_MIN + Math.random() * (C_MAX - C_MIN);
	d = D_MIN + Math.random() * (D_MAX - D_MIN);
	e = E_MIN + Math.random() * (E_MAX - E_MIN);
}

///////////////////////////////////////////////
// Event listeners
///////////////////////////////////////////////
function onDocumentMouseMove( event )
{
	mouseX = event.clientX - windowHalfX;
	mouseY = event.clientY - windowHalfY;
}

function onDocumentTouchStart( event )
{
	if ( event.touches.length == 1 )
	{
		event.preventDefault();
		mouseX = event.touches[ 0 ].pageX - windowHalfX;
		mouseY = event.touches[ 0 ].pageY - windowHalfY;
	}
}

function onDocumentTouchMove( event )
{
	if ( event.touches.length == 1 )
	{
		event.preventDefault();
		mouseX = event.touches[ 0 ].pageX - windowHalfX;
		mouseY = event.touches[ 0 ].pageY - windowHalfY;
	}
}

function onWindowResize( event )
{
	windowHalfX = window.innerWidth / 2;
	windowHalfY = window.innerHeight / 2;
	camera.aspect = window.innerWidth / window.innerHeight;
	camera.updateProjectionMatrix();
	renderer.setSize( window.innerWidth, window.innerHeight );
}

function onKeyDown(event)
{
	if(event.keyCode == 38 && speed < 20) speed += .5;

	if(event.keyCode == 40 && speed > 0) speed -= .5;

	if(event.keyCode == 37) rotationSpeed += .001;

	if(event.keyCode == 39) rotationSpeed -= .001;
}

function showHideAbout()
{
	if (document.getElementById('about').style.display == "block")
	{
		document.getElementById('about').style.display = "none";
	}
	else
	{
		document.getElementById('about').style.display = "block";
	}
}
</script><div><canvas width="1680" height="889" id="aeaoofnhgocdbnbeljkmbjdmhbcokfdb-mousedown"></canvas><div style="cursor: pointer; width: 80px; opacity: 0.9; z-index: 10001; position: absolute; bottom: 5px; right: 5px; "><div style="background-color: rgb(8, 8, 24); padding-top: 2px; padding-right: 0px; padding-bottom: 3px; padding-left: 0px; "><div style="font-family: Helvetica, Arial, sans-serif; text-align: left; font-size: 9px; color: rgb(0, 255, 255); margin-top: 0px; margin-right: 0px; margin-bottom: 1px; margin-left: 3px; "><span style="font-weight:bold">26 FPS</span> (3-58)</div><canvas width="74" height="30" style="display: block; margin-left: 3px; "></canvas></div><div style="background-color: rgb(8, 24, 8); padding-top: 2px; padding-right: 0px; padding-bottom: 3px; padding-left: 0px; display: none; "><div style="font-family: Helvetica, Arial, sans-serif; text-align: left; font-size: 9px; color: rgb(0, 255, 0); margin-top: 0px; margin-right: 0px; margin-bottom: 1px; margin-left: 3px; "><span style="font-weight:bold">16 MS</span> (1-2482)</div><canvas width="74" height="30" style="display: block; margin-left: 3px; "></canvas></div><div style="background-color: rgb(24, 8, 13); padding-top: 2px; padding-right: 0px; padding-bottom: 3px; padding-left: 0px; display: none; "><div style="font-family: Helvetica, Arial, sans-serif; text-align: left; font-size: 9px; color: rgb(255, 0, 128); margin-top: 0px; margin-right: 0px; margin-bottom: 1px; margin-left: 3px; "><span style="font-weight:bold">MB</span></div><canvas width="74" height="30" style="display: block; margin-left: 3px; "></canvas></div></div></div>


                            <div id="info">Martin's Hopalong Orbits Visualizer - Created using <a href="http://github.com/mrdoob/three.js" target="_blank">three.js</a>
                                    <br> [Mouse Move] Camera position
                                    <br> [Up-Down] Control speed
                                    <br> [Left-Right] Control rotation
                                    </div>

                                    <div id="aboutlink">
                                    <a href="javascript:showHideAbout()">About</a>
                                    </div>
                                    <div id="about">
                                    <b>Martin's Hopalong Orbits Visualizer</b>
                                    <p>All these complex orbits are generated iterating a generalization of this incredibly simple algorithm:</p>

                                    <p>(x, y) -&gt; (y - sign(x)*sqrt(abs(b*x - c)), a -x )</p>

                                    <p>where a, b, c are random parameters. This is known as the 'Hopalong Attractor'.</p>
                                    <p>3D rendering is done using WebGL and <a href="http://github.com/mrdoob/three.js" target="_blank">three.js</a></p>
                                            <hr>
                                            <p><a href="https://plus.google.com/113975837825449148012/">About me</a></p>
                                                    </div>

                                                    <!-- Place this tag where you want the +1 button to render -->
                                                    <div id="plusone">
                                                            <div id="___plusone_0" style="height: 24px; width: 106px; display: inline-block; text-indent: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; background-image: initial; background-attachment: initial; background-origin: initial; background-clip: initial; background-color: transparent; border-top-style: none; border-right-style: none; border-bottom-style: none; border-left-style: none; float: none; line-height: normal; font-size: 1px; vertical-align: baseline; background-position: initial initial; background-repeat: initial initial; "><iframe allowtransparency="true" frameborder="0" hspace="0" id="I1_1322250782796" marginheight="0" marginwidth="0" name="I1_1322250782796" scrolling="no" src="./Martin's Hopalong Orbits Visualizer - WebGL Experiment_files/fastbutton.htm" style="width: 106px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; border-top-style: none; border-right-style: none; border-bottom-style: none; border-left-style: none; height: 24px; position: static; left: 0px; top: 0px; visibility: visible; " tabindex="-1" vspace="0" width="100%" title="+1"></iframe></div>
                                                                    </div>
                                                                    <div id="tweet">
                                                                            <iframe src="./Martin's Hopalong Orbits Visualizer - WebGL Experiment_files/tweet_button.htm" allowtransparency="true" frameborder="0" scrolling="no" class="twitter-share-button twitter-count-horizontal" style="width: 110px; height: 20px; " title="Twitter For Websites: Tweet Button"></iframe><script type="text/javascript" src="./Martin's Hopalong Orbits Visualizer - WebGL Experiment_files/widgets.js"></script>
	                                                                                    </div>

	                                                                                    <!-- Place this render call where appropriate -->
	                                                                                    <script type="text/javascript">
	                                                                                            (function()
{
	var po = document.createElement('script');
	po.type = 'text/javascript';
	po.async = true;
	po.src = 'https://apis.google.com/js/plusone.js';
	var s = document.getElementsByTagName('script')[0];
	s.parentNode.insertBefore(po, s);
})();
</script>


<script type="text/javascript">

             var _gaq = _gaq || [];
                        _gaq.push(['_setAccount', 'UA-424666-5']);
                        _gaq.push(['_trackPageview']);

                        (function()
{
	var ga = document.createElement('script');
	ga.type = 'text/javascript';
	ga.async = true;
	ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
	var s = document.getElementsByTagName('script')[0];
	s.parentNode.insertBefore(ga, s);
})();

                        </script>


                        </body></html>